import { Singleton } from "../utils/Singleton";

/**
 * 可回收对象接口，组件实现此接口可在回收/复用时执行自定义逻辑
 */
export interface IPoolable {
  onPoolReset?(): void;
}

export class ObjectPoolManager extends Singleton<ObjectPoolManager> {
  private constructor() {
    super();
  }

  // 对象池：每个 prefab 对应一组缓存节点
  private poolMap: Map<cc.Prefab, cc.Node[]> = new Map();

  // 模板节点缓存：避免每次 reset 都 instantiate prefab
  private templateMap: Map<cc.Prefab, cc.Node> = new Map();

  // 每个 prefab 最大缓存数量（防止内存无限增长）
  private readonly MAX_POOL_SIZE = 10;

  /**
   * 获取一个可用节点，优先从对象池复用，否则实例化新节点
   */
  public getNode(prefab: cc.Prefab): cc.Node | null {
    if (!prefab) {
      cc.warn("[ObjectPoolManager] Prefab is null or invalid.");
      return null;
    }

    let pool = this.poolMap.get(prefab);
    if (!pool) {
      pool = [];
      this.poolMap.set(prefab, pool);
      console.log(
        `[ObjectPoolManager] Create new pool for prefab: ${prefab.name}`
      );
    }

    // 查找非激活的可用节点
    for (let i = 0; i < pool.length; i++) {
      const node = pool[i];
      if (!node.active && node.isValid) {
        this.resetNodeFromTemplate(node, prefab);
        node.active = true;
        return node;
      }
    }

    // 没有可用节点，创建新节点
    const newNode = cc.instantiate(prefab);
    this.resetNodeFromTemplate(newNode, prefab);
    newNode.active = true;

    // 标记该节点属于哪个 prefab，用于快速回收
    (newNode as any).__prefabRef = prefab;

    pool.push(newNode);
    return newNode;
  }

  /**
   * 回收节点到对象池
   */
  public putNode(node: cc.Node): void {
    if (!node || !node.isValid) {
      return;
    }

    // 通过 __prefabRef 快速定位所属 prefab
    const prefab = (node as any).__prefabRef as cc.Prefab;
    if (!prefab) {
      cc.warn(`[ObjectPoolManager] 节点未标记 prefab，无法回收：${node.name}`);
      return;
    }

    const pool = this.poolMap.get(prefab);
    if (!pool || !pool.includes(node)) {
      cc.warn(`[ObjectPoolManager] 尝试回收未注册的节点：${node.name}`);
      return;
    }

    // 1. 通知所有 IPoolable 组件进行重置
    const components = node.getComponents("IPoolable") as IPoolable[];
    components.forEach((comp) => {
      comp.onPoolReset?.();
    });

    // 2. 停止动画
    const anim = node.getComponent(cc.Animation);
    if (anim) {
      anim.stop();
    }

    // 3. 移除所有事件监听
    node.targetOff(node);

    // 4. 从父节点移除
    if (node.parent) {
      node.removeFromParent();
    }

    // 5. 重置基础状态
    node.active = false;

    // 6. 限制池子大小，防止无限缓存
    this.drainPool(prefab, this.MAX_POOL_SIZE);
  }

  /**
   * 从 prefab 模板恢复节点的初始状态（位置、缩放、颜色等）
   */
  private resetNodeFromTemplate(node: cc.Node, prefab: cc.Prefab): void {
    const template = this.getOrCreateTemplate(prefab);

    // 恢复基础属性
    node.setPosition(template.position);
    node.angle = template.angle;
    node.scaleX = template.scaleX;
    node.scaleY = template.scaleY;
    node.opacity = template.opacity;
    node.group = template.group;
    node.color = template.color.clone();

    // 物理组件（如有）
    const templateRb = template.getComponent(cc.RigidBody);
    if (templateRb) {
      const nodeRb = node.getComponent(cc.RigidBody);
      if (nodeRb) {
        nodeRb.linearVelocity = cc.v2(templateRb.linearVelocity);
        nodeRb.angularVelocity = templateRb.angularVelocity;
      }
    }

    // 通知组件进行复用前的重置
    const components = node.getComponents("IPoolable") as IPoolable[];
    components.forEach((comp) => {
      comp.onPoolReset?.();
    });
  }

  /**
   * 获取或创建 prefab 的模板节点（只实例化一次）
   */
  private getOrCreateTemplate(prefab: cc.Prefab): cc.Node {
    if (this.templateMap.has(prefab)) {
      return this.templateMap.get(prefab)!;
    }

    const template = cc.instantiate(prefab);
    template.name = `TEMPLATE_${prefab.name}`;
    this.templateMap.set(prefab, template);
    return template;
  }

  /**
   * 限制对象池大小，超出部分销毁
   */
  private drainPool(prefab: cc.Prefab, maxCount: number): void {
    const pool = this.poolMap.get(prefab);
    if (!pool) return;

    while (pool.length > maxCount) {
      const node = pool.pop();
      if (node && node.isValid) {
        node.destroy();
      }
    }
  }

  /**
   * 清空指定 prefab 的对象池
   */
  public clearByPrefab(prefab: cc.Prefab): void {
    const pool = this.poolMap.get(prefab);
    if (pool) {
      pool.forEach((node) => {
        if (node && node.isValid) {
          node.destroy();
        }
      });
      this.poolMap.delete(prefab);
    }
  }

  /**
   * 清空所有对象池（通常在场景切换或资源释放时调用）
   */
  public clearAllPools(): void {
    this.poolMap.forEach((nodes, prefab) => {
      nodes.forEach((node) => {
        if (node && node.isValid) {
          node.destroy();
        }
      });
    });
    this.poolMap.clear();

    // 可选：清理模板节点（如果资源已释放）
    // this.templateMap.forEach(template => template.destroy());
    // this.templateMap.clear();
  }

  /**
   * 获取对象池中某个 prefab 的缓存数量
   */
  public getPoolSize(prefab: cc.Prefab): number {
    const pool = this.poolMap.get(prefab);
    return pool ? pool.length : 0;
  }
}
